package com.tcs.teg.dao.accounts.impl;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.Criteria;
import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.Restrictions;
import org.hibernate.stat.Statistics;
import org.springframework.dao.DataAccessException;
import org.springframework.orm.ObjectRetrievalFailureException;
import org.springframework.orm.hibernate3.HibernateTemplate;
import org.springframework.transaction.annotation.Transactional;

import com.tcs.teg.dao.accounts.IAccountDao;
import com.tcs.teg.domain.accounts.Account;
import com.tcs.teg.domain.accounts.AccountCriteria;
import com.tcs.teg.domain.accounts.Beneficiary;
import common.money.Percentage;

/**
 * An account dao that uses Hibernate to find accounts.
 */

public class HibernateAccountDaoImpl  implements IAccountDao {

	private SessionFactory sessionFactory;
	
	private HibernateTemplate hibernateTemplate;
	
	private static final Log logger = LogFactory.getLog(HibernateAccountDaoImpl.class);

	/**
	 * Creates a new Hibernate account dao.
	 * @param sessionFactory the Hibernate session factory
	 */

	public HibernateAccountDaoImpl() {	
	}

	
	public HibernateAccountDaoImpl(SessionFactory sessionFactory) {
		logger.debug("INSTANCE ............................... " +this);
		this.sessionFactory = sessionFactory;
		hibernateTemplate = new HibernateTemplate(sessionFactory);
	}

	@Transactional(readOnly = true)
	@SuppressWarnings("unchecked")
	public List<Account> getAllAccounts() {
		logger.debug("INSTANCE ............................... " +this);
		//return getCurrentSession().createQuery("from Account").list();
		return hibernateTemplate.find("from Account");
		
	}

	@Transactional(readOnly = true)
	@SuppressWarnings("unchecked")
	public List<String> getAccountNames(String searchStr) {
		logger.debug("INSTANCE ............................... " +this);
		List<String> accountNames = new ArrayList<String>();
		try {
	        // Creating a Criteria instance                        
	        Criteria crit = getCurrentSession().createCriteria(Account.class);
	        Criterion cr1 = null; 
	       
	        cr1 = Restrictions.like("name","%"+searchStr+"%");	        
	        crit.add(cr1);
	        	
	        List<Account>  accountList = (List<Account>)crit.list();
	        Iterator itr = accountList.iterator();                        
	        while (itr.hasNext()) {                        
	        	Account account = (Account) itr.next(); 
	        	accountNames.add(account.getName());
	        }
        }
        catch(HibernateException e){
        	e.printStackTrace();
        }
		return accountNames;
	}
	
	@Transactional(readOnly = true)
	@SuppressWarnings("unchecked")
	public List<String> getContactNumbers(String searchStr) {
		logger.debug("INSTANCE ............................... " +this);
		List<String> contactNumber = new ArrayList<String>();
		try {
	        // Creating a Criteria instance                        
	        Criteria crit = getCurrentSession().createCriteria(Account.class);
	        Criterion cr1 = null; 
	       
	        cr1 = Restrictions.like("contactNumber","%"+searchStr+"%");	        
	        crit.add(cr1);
	        	
	        List<Account>  accountList = (List<Account>)crit.list();
	        Iterator itr = accountList.iterator();                        
	        while (itr.hasNext()) {                        
	        	Account account = (Account) itr.next(); 
	        	contactNumber.add(account.getContactNumber());
	        }
        }
        catch(HibernateException e){
        	e.printStackTrace();
        }
		return contactNumber;
	}
	
	
	
	
	
	
	@Transactional(readOnly = true)
	public Account getAccount(Integer entityId) throws ObjectRetrievalFailureException{
		logger.debug("INSTANCE ............................... " +this);
		Account acc = (Account) getCurrentSession().load(Account.class, entityId);		
		return acc;
	}

	@Transactional
	public void update(Account account) {
		if (logger.isDebugEnabled()) {
			logger.debug("Updating Account in DAO-------------------"+account);
		}
		//save(account);
		//getCurrentSession().update(account);
		hibernateTemplate.saveOrUpdate(account);
		
		account.setCreditCardNumber("1111111111");
		if (logger.isDebugEnabled()) {
			logger.debug("Updating Second time Account in DAO-------------------"+account);
		}
		
		hibernateTemplate.saveOrUpdate(account);
		
	}
	
	
	@Transactional
	public void createAccount(Account account) {
		if (logger.isDebugEnabled()) {
			logger.debug("Creating Account in DAO-------------------"+account);
		}
		//generate the primary key
		account.setEntityId(getNextAccountId());
		int lastBenId = getNextBeneficiaryId();
		List<Beneficiary> benList = account.getBeneficiaries();
		for(Beneficiary ben : benList){
			ben.setEntityId(lastBenId++);
		}
		getCurrentSession().persist(account);
	}

	@Transactional
	public void updateBeneficiaryAllocationPercentages(Integer accountId, Map<String, Percentage> allocationPercentages) {
		Account account = getAccount(accountId);
		for (Entry<String, Percentage> entry : allocationPercentages.entrySet()) {
			account.getBeneficiary(entry.getKey()).setAllocationPercentage(entry.getValue());
		}
	}

	@Transactional
	public void addBeneficiary(Integer accountId, String beneficiaryName) {
		getAccount(accountId).addBeneficiary(beneficiaryName, Percentage.zero());
	}

	@Transactional
	public void removeBeneficiary(Integer accountId, String beneficiaryName, Map<String, Percentage> allocationPercentages) {
		getAccount(accountId).removeBeneficiary(beneficiaryName);
		updateBeneficiaryAllocationPercentages(accountId, allocationPercentages);
	}

	/**
	 * Returns the session associated with the ongoing reward transaction.
	 * @return the transactional session
	 */
	protected Session getCurrentSession() {
		Session s = sessionFactory.getCurrentSession();
		return s;
	}
	
	@SuppressWarnings("unchecked")
	@Transactional
    public List<Account> search(AccountCriteria criteria) throws DataAccessException {
		String critName = criteria.getName();
		String critNumber = criteria.getContactNumber();		
    	if (logger.isDebugEnabled()) {
				logger.debug("Searching Account -------------------"+critName);
				logger.debug("Searching Account -------------------"+critNumber);
			}
		Account account = null;
        List<Account> results = null; 
        try {
	        // Creating a Criteria instance                        
	        Criteria crit = getCurrentSession().createCriteria(Account.class);
	        Criterion cr1 = null;
	        Criterion cr2 = null;
	        // Two Criteria Condition
	        if(critName!=null && critName.trim().length()>0){	        	
	        	cr1 = Restrictions.like("name",  "%"+critName+"%");
	        }	
	        if(critNumber!=null && critNumber.trim().length()>0 ){
	            cr2 = Restrictions.like("contactNumber","%"+critNumber+"%");
	        }    
	        if(cr1!=null && cr2!=null) {
	        	crit.add(Restrictions.and(cr1, cr2));
	        }else if(cr1!=null){
	        	crit.add(cr1);
	        }else if (cr2!=null){
	        	crit.add(cr2);
	        }	
	        results = (List<Account>)crit.list();
	        Iterator itr = results.iterator();                        
	        while (itr.hasNext()) {                        
	        	account = (Account) itr.next(); 	        	
	        }
        }
        catch(HibernateException e){
        	//e.printStackTrace();
        	throw hibernateTemplate.convertHibernateAccessException(e);
        }
        return results;
        
    }
	
	public Statistics getStatistics(){
		return hibernateTemplate.getSessionFactory().getStatistics();
	}
	
	
	private Integer getNextAccountId(){
		String SQL_QUERY = "select max(entityId)from Account account";
		Query query = getCurrentSession().createQuery(SQL_QUERY);
		List<Integer> list = query.list();
		logger.debug("Last AccountEntity Id: " + list.get(0));
		return list.get(0)+1;
		
	}
	
	private Integer getNextBeneficiaryId(){
		String SQL_QUERY = "select max(entityId)from Beneficiary beneficiary";
		Query query = getCurrentSession().createQuery(SQL_QUERY);
		List<Integer> list = query.list();
		logger.debug("Last BeneficiaryEntity Id: " + list.get(0));
		return list.get(0)+1;
		
	}
	
}